
package urbanparks;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import exceptions.FileFormatException;

/**
 * User class for loading and saving user information.
 * The class contains stores information on different
 * users (manager/staff/volunteer), including blackball
 * status.
 * 
 * @author Andrew Boguk
 * @version 1.0
 */
public class UserSet {
  /** Minimum parameters before skipping parsed line. */
  private final static int MIN_PARAMETERS = 4;
  /** The field for permission value of user. */
  private final static int FIELD_PERMISSION = 3;
  /** Volunteer ID. */
  public final static int USER_VOLUNTEER = 1;
  /** User ID. */
  public final static int USER_STAFF = 0;
  /** Manager ID. */
  public final static int USER_MANAGER = 2;
  /** File containing Park information. */
  private final File my_file;
  /** Map containing User objects, keys are permissions. */
  public final Map<Integer, List<User>> my_users;

  
  /**
   * Constructor that creates a map of:
   * Volunteer, Staff, and Managers.
   * 
   * Parameter is the file from which to load the user
   * information from, improper data or incorrectly formatted
   * lines will be skipped when parsing.
   * 
   * @param the_user_file CSV file to load user information from.
   */
  public UserSet(File the_user_file) {
    my_file = the_user_file;
    my_users = new HashMap<Integer, List<User>>();

    my_users.put(USER_VOLUNTEER, new LinkedList<User>());
    my_users.put(USER_STAFF, new LinkedList<User>());
    my_users.put(USER_MANAGER, new LinkedList<User>());
     
    loadFromFile();
  }

  /**
   * Populate map with lowercase keys of of permission values.
   * Keys are mapped to lists associated with the permission
   * value (level).
   * 
   * Parsed lines smaller than required parameters are
   * skipped. Parsed lines larger than required parameters
   * are cut off at TOTAL_PARAMETERS.
   */
  public void loadFromFile() { 
    for (Integer key : my_users.keySet())
    {
      my_users.get(key).clear();
    }
    
    final Scanner in;
    String[] values;

    try {
      in = new Scanner(my_file);
    } catch (final FileNotFoundException e) {
      throw new FileFormatException("user file");
    }

    while (in.hasNextLine()) {

      values = in.nextLine().split(",");
      
      if (values.length < MIN_PARAMETERS) {
        continue;
      }

      int permissions = -1;
      try {
        permissions =
            Integer.parseInt(values[FIELD_PERMISSION]
                .trim());
      } catch (NumberFormatException e) {
        continue;
      }

      // Invalid permission ID.
      if (permissions == -1 ||
          my_users.get(permissions) == null) {
        continue;
      }

      for (int i = 0; i < values.length; i++) {
        values[i] = values[i].trim();
      }

      addUser(values, permissions);
    }
  }

  /**
   * Helper method for adding users to map 
   * by permission type.
   * 
   * @param values Values from a single user.
   * @param the_permissions Permission of that user.
   */
  private void addUser(String[] values, int the_permissions) {
    int i = 0;
    User user = null;
    
    switch (the_permissions) {
      case USER_STAFF:
        user =
            new User(values[i++], values[i++], values[i++],
                the_permissions);
        break;
      case USER_VOLUNTEER:
        boolean flag =
            getBlackballStatus(values[FIELD_PERMISSION + 2]);
        user =
            new Volunteer(values[i++], values[i++],
                values[i++], the_permissions,
                values[i + 1], flag);
        break;
      case USER_MANAGER:
        user =
            new ParkManager(values[i++], values[i++],
                values[i++], the_permissions, values[i + 1]);
        break;
    }
    
    if (user != null) {
      my_users.get(the_permissions).add(user);
    }
  }

  /**
   * Helper method to reduce clutter, checks if string
   * matches the correct boolean.
   * 
   * @param the_blackball_status String with boolean expression.
   * @return True if provided string contains true, false otherwise.
   */
  private boolean getBlackballStatus(String the_blackball_status) {
    boolean result = false;

    if (the_blackball_status.equalsIgnoreCase("true")) {
      result = true;
    }

    return result;
  }

  /**
   * Searches through all permission values to find user with
   * specified email address.
   * 
   * @param the_user_email Email to search volunteers with.
   * @return User with specified email, otherwise null.
   */
  public User getUser(final String the_user_email) {
    User temp = null;

    List<User> unionall = new LinkedList<User>();
    for (Integer i : my_users.keySet()) {
      unionall.addAll(my_users.get(i));
    }

    for (User user : unionall) {
      if (user.getEmailAddress().equalsIgnoreCase(
          the_user_email)) {
        temp = user;
      }
    }

    return temp;
  }

  /**
   * @return List with all park managers.
   */
  public List<User> getParkManagers() {
    return my_users.get(USER_MANAGER);
  }

  /**
   * @return List with all staff members.
   */
  public List<User> getStaffMembers() {
    return my_users.get(USER_STAFF);
  }

  /**
   * @return List with all volunteers.
   */
  public List<User> getVolunteers() {
    return my_users.get(USER_VOLUNTEER);
  }

  /**
   * @return List with all blackballed volunteers.
   */
  public List<User> getBlackballedVolunteers() {
    final List<User> temp = new LinkedList<User>();

    for (User user : my_users.get(USER_VOLUNTEER)) {
      if (user.getPermissionLevel() == USER_VOLUNTEER) {
        if (((Volunteer) user).isBlackballed()) {
          temp.add(user);
        }
      }
    }

    return temp;
  }

  /**
   * Write to file by clearing CSV text and re-entering
   * each park, information separated by commas and users
   * separated by system newline character.
   */
  public void writeFile() {
    try {
      FileOutputStream out =
          new FileOutputStream(my_file, false);
      
      Volunteer v;
      for (User u : my_users.get(USER_VOLUNTEER)) {
        v = (Volunteer) u;
        out.write(v.getFileString().getBytes());
        out.write(System.getProperty("line.separator").getBytes());
      }
      
      ParkManager pm;
      for (User u : my_users.get(USER_MANAGER)) {
        pm = (ParkManager) u;
        out.write(pm.getFileString().getBytes());
        out.write(System.getProperty("line.separator").getBytes());
      }
      
      for (User u : my_users.get(USER_STAFF)) {
        out.write(u.getFileString().getBytes());
        out.write(System.getProperty("line.separator").getBytes());
      }

      out.close();
    } catch (IOException e) {
      // TODO Problem closing file.
      //e.printStackTrace();
    }
  }
}
